﻿using System.Text.Encodings.Web;
using System.Text.Json;

using zijian666.AnyExtensions;

namespace zijian666.Updater;
partial class LocalData
{
    /// <summary>
    /// 读取 JSON 时的序列化选项。
    /// 属性名不区分大小写，跳过注释，允许结尾逗号。
    /// </summary>
    private static readonly JsonSerializerOptions READ_OPTIONS = new()
    {
        PropertyNameCaseInsensitive = true,
        ReadCommentHandling = JsonCommentHandling.Skip,
        AllowTrailingCommas = true
    };

    /// <summary>
    /// 写入 JSON 时的选项。
    /// 使用缩进和宽松的转义编码。
    /// </summary>
    private static readonly JsonWriterOptions WRITER_OPTIONS = new()
    {
        Indented = true,
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };

    /// <summary>
    /// 从 JSON 字符串或文件路径加载 <see cref="LocalData"/> 实例。
    /// </summary>
    /// <param name="jsonOrPath">JSON 字符串或文件路径。</param>
    /// <returns>解析得到的 <see cref="LocalData"/> 实例。</returns>
    /// <exception cref="InvalidOperationException">无法解析版本描述文件时抛出。</exception>
    public async ValueTask LoadFromJson(string jsonOrPath)
    {
        var json = await Helper.ReadString(jsonOrPath);
        var dict = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json, READ_OPTIONS)
                   ?? throw new InvalidOperationException("无法解析本地数据文件: " + jsonOrPath);
        var stringDict = dict.ToDictionary(kv => kv.Key, kv =>
        {
            return kv.Value.ValueKind switch
            {
                JsonValueKind.String => kv.Value.GetString() ?? string.Empty,
                JsonValueKind.Number => kv.Value.GetRawText(),
                JsonValueKind.True => "true",
                JsonValueKind.False => "false",
                JsonValueKind.Null => string.Empty,
                _ => kv.Value.GetRawText()
            };
        });
        await this.LoadData(stringDict, DataLoadMode.Merge);
    }

    /// <summary>
    /// 将 <see cref="LocalData"/> 实例保存为 JSON 文件。
    /// </summary>
    /// <param name="data">要保存的数据。</param>
    /// <param name="filePath">目标文件路径。</param>
    /// <param name="hasComment">是否包含注释。</param>
    /// <returns>异步任务。</returns>
    public async ValueTask SaveToJson(string filePath, bool hasComment = true)
    {
        this.Flush();
        await using var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
        await using var writer = new Utf8JsonWriter(fs, WRITER_OPTIONS);

        writer.WriteStartObject();
        foreach (var item in this._data.Values)
        {
            if (item.IsSystem)
            {
                continue;
            }

            var name = item.Name.ToSnakeCase();
            var value = item.RawString;
            var desc = item.Description;
            if (hasComment && !string.IsNullOrEmpty(desc))
            {
                writer.WriteCommentValue(desc);
            }
            writer.WriteString(name, value);
        }
        writer.WriteEndObject();
        await writer.FlushAsync();
    }

}
